﻿#include "Mp3Decode.h"

#include "FormatConvert.h"
#include "LoopBuffer.h"
#include <QDebug>
#include <QThread>
#include <QAbstractEventDispatcher>

#include "AudioConvert.h"

extern "C"
{
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/avutil.h"
#include "libavutil/mathematics.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#include "libavutil/channel_layout.h"
#include "libavutil/samplefmt.h"
#include "libavdevice/avdevice.h"  //摄像头所用
#include "libavfilter/avfilter.h"
#include "libavutil/error.h"
#include "libavutil/mathematics.h"
#include "libavutil/time.h"
#include "inttypes.h"
#include "stdint.h"
};

Mp3Decode::Mp3Decode(const QString &file, QObject *parent) : QObject(parent),m_file(file)
{
    av_register_all();
    parseMp3MetaInfo();
}

Mp3Decode::~Mp3Decode()
{
    qDebug() << "Mp3Decode delete";
}

void Mp3Decode::setParam(int dstSampleRate, LoopBuffer *buffer)
{
    m_dstSampleRate = dstSampleRate;
    m_buffer = buffer;
}

void Mp3Decode::parseMp3MetaInfo()
{
    char path[256] = {0};
    memcpy(path,m_file.toStdString().c_str(),m_file.size());
    qDebug() << path << m_file;

    AVFormatContext *mp3Ctx = avformat_alloc_context();

    if (avformat_open_input(&mp3Ctx,path,NULL,NULL) != 0) {
        qDebug() << "open format context error";
        return;
    }

    if (avformat_find_stream_info(mp3Ctx,NULL) < 0) {
        qDebug() << "find stream error";
        return;
    }


    av_dump_format(mp3Ctx, 0, path, false);

    int videoIndex = -1,audioIndex = -1;
    for (unsigned int i = 0; i < mp3Ctx->nb_streams; ++i) {
        if (mp3Ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
            videoIndex = i;
        if (mp3Ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
            audioIndex = i;
    }

    if (audioIndex == -1)
        return;
    qDebug() << "video" << videoIndex << "audio" << audioIndex;

    m_srcChannels = mp3Ctx->streams[audioIndex]->codec->channels;
    m_srcSampleRate = mp3Ctx->streams[audioIndex]->codec->sample_rate;
    m_srcFormat = mp3Ctx->streams[audioIndex]->codec->sample_fmt;
    m_srcDuration = mp3Ctx->streams[audioIndex]->duration;

    avformat_free_context(mp3Ctx);
}

void Mp3Decode::run()
{
    m_isStop = false;
    if (!m_buffer) {
        qWarning("buffer is empty");
        return;
    }
    char path[256] = {0};
    memcpy(path,m_file.toStdString().c_str(),m_file.size());
    qDebug() << path << m_file;

    AVFormatContext *mp3Ctx = avformat_alloc_context();

    if (avformat_open_input(&mp3Ctx,path,NULL,NULL) != 0) {
        qDebug() << "open format context error";
        return;
    }

    if (avformat_find_stream_info(mp3Ctx,NULL) < 0) {
        qDebug() << "find stream error";
        return;
    }


    av_dump_format(mp3Ctx, 0, path, false);

    int videoIndex = -1,audioIndex = -1;
    for (unsigned int i = 0; i < mp3Ctx->nb_streams; ++i) {
        if (mp3Ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
            videoIndex = i;
        if (mp3Ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
            audioIndex = i;
    }

    if (audioIndex == -1)
        return;
    qDebug() << "video" << videoIndex << "audio" << audioIndex;

    AVCodecContext *codecCtx = mp3Ctx->streams[audioIndex]->codec;
    AVCodec *codec = avcodec_find_decoder(codecCtx->codec_id);

    if (codec == NULL){
        qDebug() << "codec error";
        return;
    }

    qDebug() << "avcodec context" << codecCtx->sample_rate << codecCtx->bit_rate << codecCtx->channels << codecCtx->sample_fmt;
    qDebug() << "info" << codec->name ;

    if (avcodec_open2(mp3Ctx->streams[audioIndex]->codec,
                      avcodec_find_decoder(mp3Ctx->streams[audioIndex]->codec->codec_id),
                      NULL) < 0) {
        qDebug() << "open2 error";
        return;
    }
    AVPacket *pkt = av_packet_alloc();
    int got_audio;

    while(av_read_frame(mp3Ctx,pkt) >= 0 && !m_isStop) {
        if (pkt->stream_index == audioIndex) {
            AVFrame *frame = av_frame_alloc();
            int ret = avcodec_decode_audio4(codecCtx,frame,&got_audio,pkt);
            if (ret < 0) {
                qDebug() << "error";
                return;
            }
            av_free_packet(pkt);

            if (got_audio > 0) {
#if 1   // use api 1
                char buf[8102] = {0};
                int byteSize;
                int retSample;
                if (FormatConvert::audio_rescale(frame,m_dstSampleRate,2,AV_SAMPLE_FMT_S16,(uint8_t*)buf,&byteSize,&retSample) < 0) {
                    qDebug() << "转换错误";
                    continue;
                }

//                qDebug() << "src nb sample" << frame->nb_samples << "dst nb sample" << retSample << "byte size:" << byteSize;

                int writeValue = 0;
                do {
                    QThread * currentThread = QThread::currentThread();
                    QAbstractEventDispatcher *dispatcher = currentThread->eventDispatcher();
                    dispatcher->processEvents(QEventLoop::AllEvents);
                    writeValue = m_buffer->writeData((const char *)buf,byteSize);
                    if (writeValue == 0) {
                        /* 应该依据每帧的大小得到休眠事件，比如8000Hz，2B，2B，那么一秒数据为32000B,小于这个，并休眠超过1s，那么就会造成播放断断续续 */
                        QThread::msleep(1000);
                    }
                } while (writeValue <= 0 && !m_isStop);
#endif

#if 0   // use api 2
                AVFrame *audioFrame = av_frame_alloc();
                audioFrame->sample_rate = m_dstSampleRate;
                audioFrame->channels = 2;
                audioFrame->format = AV_SAMPLE_FMT_S16;
                audioFrame->channel_layout = av_get_default_channel_layout(audioFrame->channels);
                audioFrame->nb_samples = av_rescale_rnd(frame->nb_samples, audioFrame->sample_rate, frame->sample_rate, AV_ROUND_UP);
                if (av_frame_get_buffer(audioFrame,0) < 0) {
                    Q_ASSERT(false);
                }
                if (FormatConvert::audio_rescale(frame,audioFrame) < 0) {
                    continue;
                }
                int writeValue = 0;
                do {
                    writeValue = m_buffer->writeData((const char*)audioFrame->data[0],audioFrame->linesize[0]);
                    if (writeValue == 0) {
                        /* 应该依据每帧的大小得到休眠事件，比如8000Hz，2B，2B，那么一秒数据为32000B,小于这个，并休眠超过1s，那么就会造成播放断断续续 */
                        QThread::msleep(500);
                    }
                } while (writeValue <= 0);
                av_frame_free(&audioFrame);
#endif

#if 0
                static AudioConvert *convert = nullptr;
                if (!convert) {
                    convert = new AudioConvert;
                    convert->setParam(frame->sample_rate,frame->channels,(AVSampleFormat)frame->format,
                                      m_dstSampleRate,2,AV_SAMPLE_FMT_S16);
                    convert->create();
                }
                if (!convert->trans(frame)) {
                    qDebug() << "转换失败";
                    continue;
                }

                AVFrame *dstFrame = nullptr;
                if ((dstFrame = convert->readFrame(1024)) != nullptr) {
                    int writeValue = 0;
                    do {
                        writeValue = m_buffer->writeData((const char*)dstFrame->data[0],dstFrame->linesize[0]);
                        if (writeValue == 0) {
                            /* 应该依据每帧的大小得到休眠事件，比如8000Hz，2B，2B，那么一秒数据为32000B,小于这个，并休眠超过1s，那么就会造成播放断断续续 */
                            QThread::msleep(500);
                        }
                    } while (writeValue <= 0);
                    av_frame_unref(dstFrame);
                    av_frame_free(&dstFrame);
                }
#endif
            }
            av_frame_free(&frame);
        }
    }

    av_packet_free(&pkt);
    avformat_free_context(mp3Ctx);

    qDebug() << "mp3 end";
}

int Mp3Decode::getSrcChannels() const
{
    return m_srcChannels;
}

int Mp3Decode::getSrcFormat() const
{
    return m_srcFormat;
}

int Mp3Decode::getSrcSampleRate() const
{
    return m_srcSampleRate;
}

void Mp3Decode::stop()
{
    qDebug() << "Mp3Decode stop";
    m_isStop = true;
}
